package com.liuqi.knet.remoting.transport;

import com.liuqi.knet.remoting.Channel;
import com.liuqi.knet.remoting.ChannelHandler;
import com.liuqi.knet.remoting.RemotingException;
import com.liuqi.knet.remoting.Server;
import com.liuqi.knet.remoting.ServerConfig;
import com.liuqi.loggers.api.Logger;
import com.liuqi.loggers.facade.LoggersFacade;
import java.net.InetSocketAddress;
import java.util.Collection;

/**
 * @author liuqi
 * @date 2025/2/8 16:44
 **/
public abstract class AbstractServer extends AbstractEndpoint implements Server {

    private static final Logger logger = LoggersFacade.getLogger(AbstractServer.class);

    protected final InetSocketAddress localAddress;

    public AbstractServer(ChannelHandler handler, ServerConfig config) throws RemotingException {
        super(handler, config);

        this.localAddress = config.getLocalAddress();
        if (localAddress == null) {
            throw new NullPointerException("bind server, localAddress == null");
        }

        try {
            doBind();
            logger.info("启动服务端成功: " + localAddress);
        } catch (Throwable t) {
            throw new RemotingException("服务端启动失败, Bind: " + localAddress + ", 错误信息: ", t);
        }
    }

    @Override
    public InetSocketAddress getLocalAddress() {
        return localAddress;
    }

    /**
     * 给所有连接到此服务端的通道发送消息
     *
     * @param message   {@link com.liuqi.knet.remoting.exchange.Request} or
     *                  {@link com.liuqi.knet.remoting.exchange.Event}
     * @param sent      是否确认发送成功
     * @throws RemotingException
     */
    @Override
    public void send(Object message, boolean sent) throws RemotingException {
        Collection<Channel> channels = getChannels();
        for (Channel channel : channels) {
            if (channel.isActive()) {
                channel.send(message, sent);
            }
        }
    }

    @Override
    public void close() {
        logger.info("关闭服务端: bind: " + localAddress + ", export: " + getLocalAddress());
        super.close();
        try {
            doClose();
        } catch (Throwable e) {
            logger.warn(e.getMessage(), e);
        }
    }

    protected abstract void doBind() throws Throwable;

    protected abstract void doClose() throws Throwable;

}
